package iblink.adapter.request;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import packutil.ThreadUtil;
import packutil.event.IEvent;
import packutil.idgen.IIdGenerator;
import iblink.adapter.IBClient;
import iblink.core.HistoricalData;
import iblink.core.ISecurity;
import iblink.event.HistoricalDataResponseEvent;
import iblink.event.IBErrorEvent;
import iblink.event.IBErrorEvent.IbErrorType;
import iblink.exception.IBErrorEventException;
import iblink.exception.IBLinkException;
import iblink.util.Constants.BarSize;
import iblink.util.Constants.DataType;
import iblink.util.Constants.Duration;

public class HistoricalDataRequest extends ARequest {

	private static final int PACING_VIOLATION_SLEEP_MILLIS = 15000;

	private Log log = LogFactory.getLog(getClass());

	private final HashSet<IbErrorType> exemptError = new HashSet<IbErrorType>();
	private final ISecurity sec;
	private final Date endDateTime;
	private final Duration duration;
	private final BarSize barSize;
	private final DataType whatToShow;
	private final boolean onlyRegularTradingHours;

	private List<HistoricalData> histDat;

	protected HistoricalDataRequest(IBClient ibClient,
			IIdGenerator requestIdGenerator, ISecurity sec, Date endDateTime,
			Duration duration, BarSize barSize, DataType whatToShow,
			boolean onlyRegularTradingHours) {
		super(ibClient, requestIdGenerator);
		exemptError.add(IbErrorType.HistoricalDataPacingViolation);
		this.sec = sec;
		this.endDateTime = endDateTime;
		this.duration = duration;
		this.barSize = barSize;
		this.whatToShow = whatToShow;
		this.onlyRegularTradingHours = onlyRegularTradingHours;
	}

	@Override
	protected void execute(IBClient cli, int requestId) {

		// pacing violation code here for robustness - in theory, the throttle
		// will prevent this from ever occurring. in practice, shit happens, so
		// if the throttle fails we will just sleep and retry
		Set<IbErrorType> pacingViolationExemption = new HashSet<IbErrorType>();
		pacingViolationExemption.add(IbErrorType.HistoricalDataPacingViolation);
		IBErrorEvent lastErr = null;

		for (int i = 0; i < 4; i++) {

			cli.reqHistoricalData(requestId, sec, endDateTime, duration,
					barSize, whatToShow, onlyRegularTradingHours);
			
			IEvent response = waitForResponse(pacingViolationExemption);

			if (response instanceof IBErrorEvent) {

				lastErr = (IBErrorEvent) response;

				// this should never happen, just defensive coding to catch
				// potential bugs..
				if (!IbErrorType.HistoricalDataPacingViolation.equals(lastErr
						.getErrorType())) {
					throw new IllegalStateException(
							"Wasn't expecting this error type, only expecting pacing violation: "
									+ lastErr);
				}

				// TODO: LOG ERROR pacing violation occured, sleeping,
				// retrying
				// attempt i+1
				// TODO: improve log msg
				log.error("PACING VIOLATION, SLEEPING...");

				ThreadUtil.sleep(PACING_VIOLATION_SLEEP_MILLIS);
				continue;
			}

			histDat = ((HistoricalDataResponseEvent) response)
					.getHistDataList();
			return;
		}

		throw new IBErrorEventException(lastErr);
	}

	public List<HistoricalData> getHistoricalData() {

		if (!executed)
			throw new IBLinkException(
					"Trying to get historical data but request has not yet been executed");

		return histDat;
	}
}
